{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "2801e4d6",
   "metadata": {},
   "source": [
    "### step1：导入有关数据分析和处理的库\n",
    "**请勿修改此部分**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "id": "4367ba0a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 基本库，请勿修改\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "import math"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2c1975f9",
   "metadata": {},
   "source": [
    "### step2：导入pytorch和sklearn中有关数据集划分的部分\n",
    "**请勿修改此部分**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "id": "619e2c11",
   "metadata": {},
   "outputs": [],
   "source": [
    "# torch相关库\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.optim as optim\n",
    "from torch.utils.data import Dataset, DataLoader\n",
    "# 数据集划分\n",
    "from sklearn.model_selection import train_test_split\n",
    "# 模型评估\n",
    "from sklearn.metrics import r2_score\n",
    "from sklearn.metrics import mean_absolute_error\n",
    "from sklearn.metrics import mean_squared_error\n",
    "from sklearn.metrics import mean_absolute_percentage_error"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3434b004",
   "metadata": {},
   "source": [
    "### step3：读入数据集\n",
    "**请勿修改此部分**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "id": "b163e2fb-7e29-4473-a59f-ff82714b39ff",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>左主机转速</th>\n",
       "      <th>机舱环境温度</th>\n",
       "      <th>左主机1#缸排气温度</th>\n",
       "      <th>左主机增压器转速</th>\n",
       "      <th>左主机进口燃油流量</th>\n",
       "      <th>左主机出口燃油流量</th>\n",
       "      <th>左主机1#缸排气温度.1</th>\n",
       "      <th>左主机2#缸排气温度</th>\n",
       "      <th>左主机3#缸排气温度</th>\n",
       "      <th>左主机4#缸排气温度</th>\n",
       "      <th>左主机5#缸排气温度</th>\n",
       "      <th>左主机6#缸排气温度</th>\n",
       "      <th>左主机7#缸排气温度</th>\n",
       "      <th>左主机8#缸排气温度</th>\n",
       "      <th>左主机增压器排气温度</th>\n",
       "      <th>左主机淡水压力</th>\n",
       "      <th>左主机淡水温度</th>\n",
       "      <th>左主机海水压力</th>\n",
       "      <th>左主机滑油压力</th>\n",
       "      <th>左主机滑油温度</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0.481775</td>\n",
       "      <td>0.4390</td>\n",
       "      <td>0.371450</td>\n",
       "      <td>0.130208</td>\n",
       "      <td>0.001254</td>\n",
       "      <td>0.001096</td>\n",
       "      <td>0.371450</td>\n",
       "      <td>0.343125</td>\n",
       "      <td>0.345062</td>\n",
       "      <td>0.347275</td>\n",
       "      <td>0.339487</td>\n",
       "      <td>0.366200</td>\n",
       "      <td>0.400250</td>\n",
       "      <td>0.394813</td>\n",
       "      <td>0.445288</td>\n",
       "      <td>0.01</td>\n",
       "      <td>0.8574</td>\n",
       "      <td>0.01</td>\n",
       "      <td>0.033</td>\n",
       "      <td>0.6623</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0.465275</td>\n",
       "      <td>0.4388</td>\n",
       "      <td>0.369050</td>\n",
       "      <td>0.128942</td>\n",
       "      <td>0.001217</td>\n",
       "      <td>0.001125</td>\n",
       "      <td>0.369050</td>\n",
       "      <td>0.340275</td>\n",
       "      <td>0.343162</td>\n",
       "      <td>0.344800</td>\n",
       "      <td>0.336987</td>\n",
       "      <td>0.363000</td>\n",
       "      <td>0.397688</td>\n",
       "      <td>0.391263</td>\n",
       "      <td>0.444812</td>\n",
       "      <td>0.01</td>\n",
       "      <td>0.8573</td>\n",
       "      <td>0.01</td>\n",
       "      <td>0.033</td>\n",
       "      <td>0.6625</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>0.485350</td>\n",
       "      <td>0.4393</td>\n",
       "      <td>0.366075</td>\n",
       "      <td>0.128617</td>\n",
       "      <td>0.001254</td>\n",
       "      <td>0.001096</td>\n",
       "      <td>0.366075</td>\n",
       "      <td>0.337713</td>\n",
       "      <td>0.341413</td>\n",
       "      <td>0.342012</td>\n",
       "      <td>0.334713</td>\n",
       "      <td>0.360063</td>\n",
       "      <td>0.395000</td>\n",
       "      <td>0.387788</td>\n",
       "      <td>0.444113</td>\n",
       "      <td>0.01</td>\n",
       "      <td>0.8573</td>\n",
       "      <td>0.01</td>\n",
       "      <td>0.034</td>\n",
       "      <td>0.6625</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>0.467175</td>\n",
       "      <td>0.4390</td>\n",
       "      <td>0.363225</td>\n",
       "      <td>0.126953</td>\n",
       "      <td>0.001188</td>\n",
       "      <td>0.001067</td>\n",
       "      <td>0.363225</td>\n",
       "      <td>0.335375</td>\n",
       "      <td>0.340250</td>\n",
       "      <td>0.339587</td>\n",
       "      <td>0.332487</td>\n",
       "      <td>0.357525</td>\n",
       "      <td>0.392625</td>\n",
       "      <td>0.385213</td>\n",
       "      <td>0.443550</td>\n",
       "      <td>0.01</td>\n",
       "      <td>0.8573</td>\n",
       "      <td>0.01</td>\n",
       "      <td>0.033</td>\n",
       "      <td>0.6626</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0.474712</td>\n",
       "      <td>0.4391</td>\n",
       "      <td>0.361325</td>\n",
       "      <td>0.128400</td>\n",
       "      <td>0.001254</td>\n",
       "      <td>0.001125</td>\n",
       "      <td>0.361325</td>\n",
       "      <td>0.333162</td>\n",
       "      <td>0.338975</td>\n",
       "      <td>0.337462</td>\n",
       "      <td>0.330587</td>\n",
       "      <td>0.355088</td>\n",
       "      <td>0.391263</td>\n",
       "      <td>0.382050</td>\n",
       "      <td>0.442975</td>\n",
       "      <td>0.01</td>\n",
       "      <td>0.8576</td>\n",
       "      <td>0.01</td>\n",
       "      <td>0.034</td>\n",
       "      <td>0.6624</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      左主机转速  机舱环境温度  左主机1#缸排气温度  左主机增压器转速  左主机进口燃油流量  左主机出口燃油流量  左主机1#缸排气温度.1  \\\n",
       "0  0.481775  0.4390    0.371450  0.130208   0.001254   0.001096      0.371450   \n",
       "1  0.465275  0.4388    0.369050  0.128942   0.001217   0.001125      0.369050   \n",
       "2  0.485350  0.4393    0.366075  0.128617   0.001254   0.001096      0.366075   \n",
       "3  0.467175  0.4390    0.363225  0.126953   0.001188   0.001067      0.363225   \n",
       "4  0.474712  0.4391    0.361325  0.128400   0.001254   0.001125      0.361325   \n",
       "\n",
       "   左主机2#缸排气温度  左主机3#缸排气温度  左主机4#缸排气温度  左主机5#缸排气温度  左主机6#缸排气温度  左主机7#缸排气温度  \\\n",
       "0    0.343125    0.345062    0.347275    0.339487    0.366200    0.400250   \n",
       "1    0.340275    0.343162    0.344800    0.336987    0.363000    0.397688   \n",
       "2    0.337713    0.341413    0.342012    0.334713    0.360063    0.395000   \n",
       "3    0.335375    0.340250    0.339587    0.332487    0.357525    0.392625   \n",
       "4    0.333162    0.338975    0.337462    0.330587    0.355088    0.391263   \n",
       "\n",
       "   左主机8#缸排气温度  左主机增压器排气温度  左主机淡水压力  左主机淡水温度  左主机海水压力  左主机滑油压力  左主机滑油温度  \n",
       "0    0.394813    0.445288     0.01   0.8574     0.01    0.033   0.6623  \n",
       "1    0.391263    0.444812     0.01   0.8573     0.01    0.033   0.6625  \n",
       "2    0.387788    0.444113     0.01   0.8573     0.01    0.034   0.6625  \n",
       "3    0.385213    0.443550     0.01   0.8573     0.01    0.033   0.6626  \n",
       "4    0.382050    0.442975     0.01   0.8576     0.01    0.034   0.6624  "
      ]
     },
     "execution_count": 72,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data = pd.read_excel('data.xlsx')\n",
    "data.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e807d097",
   "metadata": {},
   "source": [
    "### step4：数据预处理\n",
    "**请勿修改此部分**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "id": "24d50ed8-dd1e-45a0-bfaa-957c3d49dce6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 读取特征标签\n",
    "X_label = data.columns[:3]\n",
    "y_label = data.columns[3:]\n",
    "\n",
    "# 读取特征值\n",
    "X = data.iloc[:, :3].values\n",
    "Y = data.iloc[:, 3:].values\n",
    "\n",
    "# 数据标准化，原始数据集应该已经是标准化的，所以不进行处理\n",
    "# scaler = StandardScaler()\n",
    "# X = scaler.fit_transform(X)\n",
    "# Y = scaler.fit_transform(Y)\n",
    "\n",
    "# 划分训练集和测试集\n",
    "X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2, random_state=42)\n",
    "\n",
    "# 转换为Tensor\n",
    "X_train = torch.tensor(X_train, dtype=torch.float32)\n",
    "X_test = torch.tensor(X_test, dtype=torch.float32)\n",
    "Y_train = torch.tensor(Y_train, dtype=torch.float32)\n",
    "Y_test = torch.tensor(Y_test, dtype=torch.float32)\n",
    "\n",
    "# 创建数据集和数据加载器\n",
    "train_data = torch.utils.data.TensorDataset(X_train, Y_train)\n",
    "test_data = torch.utils.data.TensorDataset(X_test, Y_test)\n",
    "\n",
    "# 定义数据加载器\n",
    "batch_size = 100 # 每次选取一小部分数据进行训练\n",
    "train_loader = torch.utils.data.DataLoader(dataset=train_data, batch_size=batch_size, shuffle=True)\n",
    "test_loader = torch.utils.data.DataLoader(dataset=test_data, batch_size=batch_size, shuffle=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a3825b3f",
   "metadata": {},
   "source": [
    "### step5：构建神经网络模型\n",
    "**可以根据自己的需要修改**  \n",
    "这个是一个简单的具有两个隐藏层的神经网络，包含dropout，使用ReLU作为激活函数。  \n",
    "如果需要修改模型结构，可以对以下内容进行修改：  \n",
    "- 隐藏层的数量：这里设置了两个隐藏层（fc1和fc2），可以根据需要进行添加或删除\n",
    "- 神经元的数量：每个隐藏层有128个神经元，可以根据需要进行修改\n",
    "- 激活函数：使用ReLU作为激活函数，可以根据需要进行修改为其他激活函数，如sigmoid、tanh等\n",
    "- dropout的概率：在每个隐藏层后面添加了dropout层，概率为0.2，可以根据需要进行修改，或者不使用dropout\n",
    "- 前向传播的过程：在对上面的内容修改后，需要根据新的模型结构修改前向传播的过程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "id": "d2a119ca-b8e5-4d97-ba60-6c7755cefbe5",
   "metadata": {},
   "outputs": [],
   "source": [
    "class NeuralNetwork(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(NeuralNetwork, self).__init__()\n",
    "        self.fc1 = nn.Linear(3, 128)\n",
    "        self.fc2 = nn.Linear(128, 128)\n",
    "        self.fc3 = nn.Linear(128, 17)\n",
    "        self.relu = nn.ReLU()\n",
    "        self.dropout = nn.Dropout(0.2)\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = self.fc1(x)\n",
    "        x = self.dropout(x)\n",
    "        x = self.relu(x)\n",
    "        x = self.fc2(x)\n",
    "        x = self.dropout(x)\n",
    "        x = self.relu(x)\n",
    "        x = self.fc3(x)\n",
    "        return x"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0ee17a91",
   "metadata": {},
   "source": [
    "### step6：创建模型，并设置超参数\n",
    "**可以根据自己的需要修改**  \n",
    "- 模型：这里使用的是上面定义的NeuralNetwork类，请勿修改\n",
    "- 优化器：这里使用的是Adam优化器，并设置学习率为0.001。可以根据需要修改学习率，或者使用其他优化器，比如SGD、RMSprop等\n",
    "- 损失函数：这里使用的是均方误差损失函数，请勿修改\n",
    "- 训练的总轮数：这里设置了训练的总轮数为100，可以根据需要增大或减小"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "id": "c7bee665",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建一个神经网络模型实例，这里使用的是上面定义的NeuralNetwork类，请勿修改\n",
    "model = NeuralNetwork()\n",
    "\n",
    "# 使用Adam优化器，并设置学习率为0.001。可以根据需要修改学习率，或者使用其他优化器，比如SGD、RMSprop等\n",
    "optimizer = optim.Adam(model.parameters(), lr=0.001)\n",
    "\n",
    "# 使用均方误差损失函数，请勿修改\n",
    "criterion = nn.MSELoss()\n",
    "\n",
    "# 设置训练的总轮数为100，可以根据需要增大或者减小训练轮数\n",
    "num_epochs = 100\n",
    "\n",
    "# 初始化一个列表用于存储每个批次的损失值，请勿修改\n",
    "losses = []"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d999248c",
   "metadata": {},
   "source": [
    "### step7：训练模型\n",
    "**请勿修改**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "id": "809d5cee-822f-4f93-8873-eac46825d3a5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch [10 / 100], Loss:  0.0003\n",
      "Epoch [20 / 100], Loss:  0.0004\n",
      "Epoch [30 / 100], Loss:  0.0001\n",
      "Epoch [40 / 100], Loss:  0.0003\n",
      "Epoch [50 / 100], Loss:  0.0005\n",
      "Epoch [60 / 100], Loss:  0.0002\n",
      "Epoch [70 / 100], Loss:  0.0003\n",
      "Epoch [80 / 100], Loss:  0.0001\n",
      "Epoch [90 / 100], Loss:  0.0001\n",
      "Epoch [100 / 100], Loss:  0.0001\n"
     ]
    }
   ],
   "source": [
    "# 训练模型，请勿修改\n",
    "for epoch in range(num_epochs):\n",
    "    for i, (data_x, data_y) in enumerate(train_loader):\n",
    "        model.train()\n",
    "        y_pred = model(data_x)\n",
    "        loss = criterion(y_pred, data_y)\n",
    "        \n",
    "        optimizer.zero_grad()\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "\n",
    "        losses.append(loss.item())\n",
    "        \n",
    "    if ((epoch + 1) % 10 == 0):\n",
    "        print(f'Epoch [{epoch + 1} / {num_epochs}], Loss: {loss.item(): .4f}')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7817a7af",
   "metadata": {},
   "source": [
    "### step8：评估模型\n",
    "**请勿修改**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "id": "97e1c07d-4605-4b29-98d0-d7e798bea9f1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Neural Network MSE: 3.859446211148531e-05\n",
      "Neural Network R2: 0.9915041327476501\n",
      "Neural Network MAE: 0.0023296133642414472\n",
      "Neural Network RMSE: 0.006212444133469959\n"
     ]
    }
   ],
   "source": [
    "model.eval()\n",
    "with torch.no_grad():\n",
    "    Y_pred_nn = model(X_test)\n",
    "            \n",
    "# 计算MSE\n",
    "nn_mse = mean_squared_error(Y_test, Y_pred_nn)\n",
    "print(f\"Neural Network MSE: {nn_mse}\")\n",
    "# 计算R2\n",
    "nn_r2 = r2_score(Y_test.numpy(), Y_pred_nn.numpy())\n",
    "print(f\"Neural Network R2: {nn_r2}\")\n",
    "# 计算MAE\n",
    "nn_mae = mean_absolute_error(Y_test, Y_pred_nn)\n",
    "print(f\"Neural Network MAE: {nn_mae}\")\n",
    "# 计算RMSE\n",
    "nn_rmse = math.sqrt(nn_mse)\n",
    "print(f\"Neural Network RMSE: {nn_rmse}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "id": "067f8d26-8b20-4b90-bbdd-ca5206e18b49",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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t3c7xXY2LVq0cPv6dBg6HXYu9lgBdQ0BCMDPuuqqq7R79269/fbb6tSpk9588011795db775pqvPww8/rJ07dyolJUUBAQF6+umndcUVV2jTpk2XpQZ/f38NHTpUCxYs0MmTJ3Xw4EF9+eWXrtkfSXI6nZKkP/7xj1XO0qSmpqpt27Zu+w0MDLws9dUUZ5vNMsa4/lzdxwqoDQhAgE01btxYQUFB2rFjR6Vt27dvl5eXl6Kjo11tYWFhGj16tN5//31lZmaqS5cumjx5stv72rRpo0ceeUSffvqpMjIyVFZWppdffvmsNbRs2VKSzlpDeHi422Xpw4YNU25urtLS0jRv3jwZY9wC0OlTeb6+vlXO0iQnJ6tBgwbn9w26RKdno860c+dOSXItNG7ZsuVZx356u3Tq+7pjxw6Vl5dftvou9FgBdQ0BCLApb29vXXvttfr3v//tdvlzdna2Zs+erX79+rlO6xw9etTtvfXr11fbtm1VWloq6dSVUSUlJW592rRpowYNGrj6VCUqKkrx8fGaNWuW8vPzXe0ZGRn69NNPK91wMDk5WWFhYZo7d67mzp2rhIQEt1NYTZo00YABA/TGG2/o8OHDlT7vyJEj5/6mXEaHDh3SggULXF8XFhbq3XffVXx8vCIjIyVJ119/vdauXav09HRXv+LiYs2YMUMxMTGudUW33nqrcnNz9frrr1f6nJ+GrJ9zsccKqGu4DB6o495++20tXbq0Uvu4ceP0l7/8RampqerXr5/GjBkjHx8fvfHGGyotLdWLL77o6tuhQwcNGDBAPXr0UFhYmNavX6/58+froYceknRqZmPgwIG644471KFDB/n4+GjBggXKzs7WnXfeec76XnrpJQ0ePFhJSUm65557XJfBh4SEVJph8vX11S233KI5c+aouLi4yudeTZ06Vf369VPnzp117733qnXr1srOzlZ6erq+//57bdmy5SK+iz/auHGj/vWvf1Vqb9OmjZKSklxft2vXTvfcc4/WrVuniIgIvf3228rOztbMmTNdfSZMmKD3339fgwcP1u9//3uFhYVp1qxZ2rt3rz788EN5eZ36N+rIkSP17rvvavz48Vq7dq2uvPJKFRcXa/ny5RozZoxuuumm867/Uo4VUKdYeg0agGpz+jLvs70yMzONMcZs3LjRDBo0yNSvX98EBQWZq6++2qxevdpt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bYqKitz6bNmyxfTr18/4+/ubZs2ameeff94j46tqbJLMzJkzXX1OnDhhxowZYxo2bGiCgoLMzTffbA4fPuy2n3379pnBgwebwMBAEx4ebh555BFTXl7u1mfFihUmPj7e+Pn5mdatW7t9RnX5zW9+Y1q2bGn8/PxM48aNzcCBA13hp7aP7Wx+GoBq+xiHDRtmoqKijJ+fn2nWrJkZNmyY2z1yavv4jDHmP//5j+nUqZPx9/c3cXFxZsaMGW7ba/vPGWOMWbZsmZFUqW5jav8xLCwsNOPGjTMtWrQwAQEBpnXr1ubJJ590u1y9ph9DhzFn3LYRAADABlgDBAAAbIcABAAAbIcABAAAbIcABAAAbIcABAAAbIcABAAAbIcABAAAbIcABADnweFwaOHChVaXAeAyIQABqPHuvvtuORyOSq/rrrvO6tIA1FI+VhcAAOfjuuuu08yZM93a/P39LaoGQG3HDBCAWsHf31+RkZFur9NPg3Y4HJo2bZoGDx6swMBAtW7dWvPnz3d7/9atW/WLX/xCgYGBatSoke677z4dO3bMrc/bb7+tjh07yt/fX1FRUXrooYfctufm5urmm29WUFCQYmNjtWjRouodNIBqQwACUCc8/fTTuvXWW7VlyxaNGDFCd955p7799ltJUnFxsQYNGqSGDRtq3bp1mjdvnpYvX+4WcKZNm6YHH3xQ9913n7Zu3apFixapbdu2bp/xzDPP6I477tDXX3+t66+/XiNGjFBeXp5HxwngMrksj1QFgGo0atQo4+3tberVq+f2eu6554wxxkgy999/v9t7EhMTzQMPPGCMMWbGjBmmYcOG5tixY67tH3/8sfHy8jJZWVnGGGOaNm1qnnzyybPWIMk89dRTrq+PHTtmJJlPPvnkso0TgOewBghArXD11Vdr2rRpbm1hYWGuPyclJbltS0pK0ubNmyVJ3377rbp27ap69eq5tvft21dOp1M7duyQw+HQoUOHNHDgwHPW0KVLF9ef69Wrp+DgYOXk5FzskABYiAAEoFaoV69epVNSl0tgYOB59fP19XX72uFwyOl0VkdJAKoZa4AA1AlfffVVpa+vuOIKSdIVV1yhLVu2qLi42LX9yy+/lJeXl9q3b68GDRooJiZGaWlpHq0ZgHWYAQJQK5SWliorK8utzcfHR+Hh4ZKkefPmqWfPnurXr5/ee+89rV27Vm+99ZYkacSIEZo0aZJGjRqlyZMn68iRIxo7dqzuuusuRURESJImT56s+++/X02aNNHgwYNVVFSkL7/8UmPHjvXsQAF4BAEIQK2wdOlSRUVFubW1b99e27dvl3TqCq05c+ZozJgxioqK0vvvv68OHTpIkoKCgrRs2TKNGzdOvXr1UlBQkG699Va98sorrn2NGjVKJSUl+tvf/qY//vGPCg8P12233ea5AQLwKIcxxlhdBABcCofDoQULFmjo0KFWlwKglmANEAAAsB0CEAAAsB3WAAGo9TiTD+BCMQMEAABshwAEAABshwAEAABshwAEAABshwAEAABshwAEAABshwAEAABshwAEAABshwAEAABs5/8DKuwtQNb0bBkAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(losses)\n",
    "plt.xlabel('Epoch')\n",
    "plt.ylabel('Loss')\n",
    "plt.title('Loss over Epochs')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c3b352f7",
   "metadata": {},
   "source": [
    "### step9：保存模型\n",
    "**请勿修改**\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 107,
   "id": "0c9a291a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 保存为onnx模型\n",
    "# model = torch.jit.trace(model)\n",
    "model.eval()\n",
    "dummy_input = torch.randn(1, 3)\n",
    "torch.onnx.export(model, dummy_input, \"model.onnx\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 108,
   "id": "2432b628",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ONNX model is valid.\n"
     ]
    }
   ],
   "source": [
    "import onnx\n",
    "\n",
    "# 加载导出的 ONNX 模型\n",
    "onnx_model = onnx.load(\"model.onnx\")\n",
    "\n",
    "# 验证模型\n",
    "onnx.checker.check_model(onnx_model)\n",
    "\n",
    "# 如果没有异常抛出，说明模型是有效的\n",
    "print(\"ONNX model is valid.\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 111,
   "id": "9c6daa4d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Inference result: [array([[0.15257612, 0.00106676, 0.00113282, 0.3707162 , 0.34184608,\n",
      "        0.36519587, 0.3538729 , 0.34586918, 0.35964203, 0.40251848,\n",
      "        0.38495213, 0.47537056, 0.00985455, 0.86387384, 0.0097197 ,\n",
      "        0.03230794, 0.6644237 ]], dtype=float32)]\n"
     ]
    }
   ],
   "source": [
    "import onnxruntime as ort\n",
    "import numpy as np\n",
    "\n",
    "# 加载 ONNX 模型\n",
    "onnx_session = ort.InferenceSession(\"model.onnx\")\n",
    "\n",
    "# 准备输入数据\n",
    "# input_data = X.astype(np.float32)  # 示例数据，形状应与训练时输入数据一致\n",
    "input_data = X[0].astype(np.float32).reshape(1, 3)\n",
    "\n",
    "# 获取模型的输入和输出名称\n",
    "input_name = onnx_session.get_inputs()[0].name\n",
    "output_name = onnx_session.get_outputs()[0].name\n",
    "\n",
    "# 进行推理\n",
    "result = onnx_session.run([output_name], {input_name: input_data})\n",
    "\n",
    "# 打印结果\n",
    "print(\"Inference result:\", result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a37d860f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 88,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X_test[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "id": "842f148e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([1985, 3])"
      ]
     },
     "execution_count": 106,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X_test.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f870cc87",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.21"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
